#region Using

using System;
using System.Web;
using System.IO;
using System.Xml;
using System.Text;
using System.Net.Mail;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using BlogEngine.Core.Providers;

#endregion

namespace BlogEngine.Core
{
	/// <summary>
	/// A post is an entry on the blog - a blog post.
	/// </summary>
	[Serializable]
	public class Post : RelateableBase<Post>, IComparable<Post>
	{
		private const string _RelativeLinkDateStamped = "post/{0}/{1}/{2}/";
		private const string _RelativeLinkNormal = "post/";

		#region Constructor

		/// <summary>
		/// The default contstructor assign default values.
		/// </summary>
		public Post()
			: base()
		{
		}

		#endregion

		#region Methods

        protected override string SetContentCore(string current, string content)
        {
            if (current != content)
            {
                MarkChanged("Content");
                HttpContext.Current.Cache.Remove("content_" + this.Id);
            }
            return content;
        }

        protected override string GetContentCore(string content)
        {
            if (string.IsNullOrEmpty(content))
            {
                return LoadPostContent();
            }

            return content;
        }

        /// <summary>
        /// Lazy loads the content of the post into cache to reduce memory footprint.
        /// </summary>
        /// <returns>The content of the post.</returns>
        private string LoadPostContent()
        {
            string key = string.Format("content_{0}", this.Id);

            if (HttpContext.Current.Cache[key] == null)
            {
                string content = PostContentProvider.SelectPostContent(this.Id);
                HttpContext.Current.Cache.Insert(key, content, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
            }

            return (string)HttpContext.Current.Cache[key];
        }


		/// <summary>
		/// Returns all posts published between the two dates.
		/// </summary>
		public static List<Post> GetPostsByDate(DateTime dateFrom, DateTime dateTo)
		{
			List<Post> list = Items.FindAll(delegate(Post p)
			{
				return (p.DateCreated.Date >= dateFrom && p.DateCreated.Date <= dateTo);
			});

			list.TrimExcess();
			return list;
		}

		/// <summary>
		/// Returns all posts written by the specified author.
		/// </summary>
		public static List<Post> GetPostsByAuthor(string author)
		{
			List<Post> list = Posts.FindAll(delegate(Post p)
			{
				string legalTitle = Utils.RemoveIllegalCharacters(p.Author);
				return Utils.RemoveIllegalCharacters(author).Equals(legalTitle, StringComparison.OrdinalIgnoreCase);
			});

			list.TrimExcess();
			return list;
		}

		/// <summary>
		/// Returns a post based on it's title.
		/// </summary>
		public static Post GetPostBySlug(string slug, DateTime date)
		{
			return Posts.Find(delegate(Post p)
			{
				if (date != DateTime.MinValue && (p.DateCreated.Year != date.Year || p.DateCreated.Month != date.Month))
				{
					if (p.DateCreated.Day != 1 && p.DateCreated.Day != date.Day)
						return false;
				}

				return slug.Equals(Utils.RemoveIllegalCharacters(p.Slug), StringComparison.OrdinalIgnoreCase);
			});
		}

		/// <summary>
		/// Returns all posts in the specified category
		/// </summary>
		public static List<Post> GetPostsByCategory(Guid categoryId)
		{
			List<Post> col = Posts.FindAll(delegate(Post p)
			{
				return p.Categories.Contains(Category.GetCategory(categoryId));
			});

			col.Sort();
			col.TrimExcess();
			return col;
		}

		/// <summary>
		/// Returns all posts tagged with the specified tag.
		/// </summary>
		public static List<Post> GetPostsByTag(string tag)
		{
			List<Post> list = Posts.FindAll(delegate(Post p)
			{
				return p.Tags.Contains(tag);
			});

			list.TrimExcess();
			return list;
		}

		#endregion

		#region Base overrides

		/// <summary>
		/// Returns the prefix to use for relative link formatting
		/// </summary>
		/// <returns></returns>
		protected override string RelativeLinkPrefix()
		{
			if (BlogSettings.Instance.TimeStampPostLinks)
			{
				return string.Format(_RelativeLinkDateStamped, DateCreated.Year, DateCreated.ToString("MM", CultureInfo.InvariantCulture), DateCreated.Day);
			}

			return _RelativeLinkNormal;
		}
		/// <summary>
		///  Returns the prefix to use for permalink URL formatting 
		/// </summary>
		/// <returns></returns>
		protected override string PermaLinkPrefix()
		{
			return "post";
		}

		/// <summary>
		/// Validates the item instance.
		/// </summary>
		/// <remarks>
		/// Each derived class should override this and ensure they call base.ValidationRules()
		/// to get a composite set of rules.
		///</remarks>
		protected override void ValidationRules()
		{
			AddRule("Content", "Content must be set", String.IsNullOrEmpty(Content));
			base.ValidationRules();
		}

		#endregion

		#region IComparable<Post> Members

		/// <summary>
		/// Compares the current object with another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// A 32-bit signed integer that indicates the relative order of the 
		/// objects being compared. The return value has the following meanings: 
		/// Value Meaning Less than zero This object is less than the other parameter.Zero 
		/// This object is equal to other. Greater than zero This object is greater than other.
		/// </returns>
		public int CompareTo(Post other)
		{
			return CompareOrderAndTitle(0, this.DateCreated, this.Title, 0, other.DateCreated, other.Title);
		}

		#endregion

		#region Data Access

		static internal BlogEngine.Core.Provider.Interfaces.IPosts PostProvider = BlogEngine.Core.Provider.BlogService.Provider.GetInterface<BlogEngine.Core.Provider.Interfaces.IPosts>();
        static internal BlogEngine.Core.Provider.Interfaces.IPostContent PostContentProvider = BlogEngine.Core.Provider.BlogService.Provider.GetInterface<BlogEngine.Core.Provider.Interfaces.IPostContent>();

        /// <summary>
        /// Saves the object to the data store (inserts, updates or deletes).
        /// </summary>
        /// <returns></returns>
        public override SaveAction Save()
        {
            SaveAction action = base.Save();
            if (action == SaveAction.Insert || action == SaveAction.Update)
                base.ClearContent();

            return action;
        }

		protected override void DataDelete()
		{
			PostProvider.Delete(this);
		}

		protected override void DataInsert()
		{
			PostProvider.Insert(this);
		}

		protected override Post DataSelect(Guid id)
		{
			return PostProvider.Select(id);
		}

		protected override void DataUpdate()
		{
			PostProvider.Update(this);
		}

		private static int Dummy = OverrideOnLoad();

		private static int OverrideOnLoad()
		{
			OnLoad = PostProvider.Fill;
			return 0;
		}

		#endregion

		/// <summary>
		/// Gets the posts.
		/// </summary>
		/// <value>The posts.</value>
		public static List<Post> Posts
		{
			get
			{
				return Items;
			}
		}

		/// <summary>
		/// Returs a post based on the specified id.
		/// </summary>
		public static Post GetPost(Guid id)
		{
			return GetById(id);
		}

	}
}
